export = {};

enum EventType { Mouse, Keyboard }

interface Event {
  timestamp: number;
}
interface MouseEvent extends Event {
  x: number;
  y: number;
}
interface KeyEvent extends Event {
  keyCode: number;
}

function listenEvent(eventType: EventType, handler: (n: Event) => void) {
  /* ... */
}
/* ↑
上面我们定义了一个函数,下面↓我们来用不同的方式来调用它,来测试函数参数的兼容性*/

/** sound
 在数学里,sound的意思是健全的、完备的,逻辑推理没有任何瑕疵的
 Typescript并不是sound的系统,是unsound的,typescript允许代码利用正确性的损失来换取效率(方便)
 >https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals
 java、c#也不是sound的,而是unsound的,大部分语言都不是,没有类型的语言更不用说,比如javascript,sound是类型系统一个的特征

 sound 就是 从语法以及逻辑上推断出你的代码有没有问题,如果它能推断出你的代码有问题并且不让你过,它就是sound的,如果它能推断出你的代码有问题但却让你过了,就是unsound */

/** 不会报错,有瑕疵的使用方式↓*/
// Unsound, but useful and common
// Unsound
// adj.不健全的； 不稳固的； 无根据的； 腐烂的
// adv.不健全地； 情况不佳地； 虚弱地； 有缺点地
// 我们定义的时候 要求handler接受的e参数为Event类型 但这里实际传递的是MouseEvent类型
// 但也可以通过 因为MouseEvent是Event的子类型,Event拥有的属性MouseEvent都拥有,故兼容(鸭式变形法)
// 或则你可以这么想 MouseEvent是不是event？ 是
listenEvent(EventType.Mouse, (e: MouseEvent) => console.log(e.x + ',' + e.y)); // 这样虽然能通过ts检测,但毕竟它原本接受的e是Event类型,并没有MouseEvent所拥有的的其它属性,listenEvent这个函数里可能就没有考虑到这种情况并有所牵连,就可能会出错

/** 不会报错 且相较上面 更不推荐的使用方式↓*/
// Undesirable alternatives in presence of soundness
// Undesirable
// adj. 不受欢迎的，讨厌的； 不合需要的； 不方便的； 不良的
// n.不受欢迎的人； 不良分子；
listenEvent(EventType.Mouse, (e: Event) => console.log((<MouseEvent>e).x + ',' + (<MouseEvent>e).y)); // 强行将e当成MouseEvent来用 可能会出错 因为Event没有x,y这俩属性
listenEvent(EventType.Mouse, <(e: Event) => void>((e: MouseEvent) => console.log(e.x + ',' + e.y))); // 这里是把 (e: MouseEvent) => console.log(e.x + ',' + e.y) 强行转化为 (e: Event) => void 类型来使用

/** 会报错↓*/
// Still disallowed (clear error). Type safety enforced for wholly incompatible types
listenEvent(EventType.Mouse, (e: number) => console.log(e));
